#ifndef SIMPLE_SUPPORT_ITERATOR_OUT_FILTER_HPP
#define SIMPLE_SUPPORT_ITERATOR_OUT_FILTER_HPP

#include <iterator> // std::iterator_traits std::output_iterator_tag
#include <utility> // std::forward

#include "../function_utils.hpp" // support::invoke

namespace simple::support
{

	// TODO: all these wrapper iteratirs need a member base() that will dig out the leaf
	template <typename It, typename Predicate>
	struct out_filter
	{
		using value_type = typename std::iterator_traits<It>::value_type;
		using difference_type = typename std::iterator_traits<It>::difference_type;
		using pointer = typename std::iterator_traits<It>::pointer;
		using reference = typename std::iterator_traits<It>::reference;
		using iterator_category = std::output_iterator_tag;
		It out;
		Predicate predicate;

		template <typename P>
		out_filter(P&& predicate, It out)
			: out(out), predicate(std::forward<P>(predicate))
		{}

		out_filter& operator++() { return *this; }
		out_filter operator++(int) { out_filter prev = *this; ++(*this); return prev; }

		class proxy
		{
			It& out;
			Predicate& predicate;

			proxy(It& out, Predicate& predicate) : out(out), predicate(predicate) {}
			friend struct out_filter;
			public:
			proxy() = delete;
			proxy(const proxy&) = delete;
			proxy(const proxy&&) = delete;
			proxy& operator=(const proxy&) = delete;
			proxy& operator=(const proxy&&) = delete;

			template <typename T>
			const proxy& operator=(T&& x) const
			{
				if(support::invoke(predicate, x))
				{
					*out = std::forward<T>(x);
					++out;
				}
				return *this;
			}

		};

		const proxy operator*() // const version should return a proxy without operator=
		{
			return {out, predicate};
		}
	};
	template <typename I, typename P> out_filter(P&&, I) -> out_filter<I, P>;

} // namespace simple::support

#endif /* end of include guard */
